package client;

import static java.nio.file.StandardWatchEventKind.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKind.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKind.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKind.OVERFLOW;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.HashMap;


public class Watcher {
	Client client;
	HashMap<WatchKey,Path> keyPathMap;
	WatchService watcherSvc;
	boolean running;
	
	//enum ChangeType{CREATE,DELETE,MODIFY,RENAME}
	
	public Watcher(Client client) throws IOException {
		this.client=client;
		keyPathMap = new HashMap<WatchKey, Path>();
		watcherSvc = FileSystems.getDefault().newWatchService();
		running=false;
	}
	
	public void pause(){
		running=false;
	}

	public boolean isRunning() {
		return running;
	}
	
	public void addDireectory(Path path) throws IOException{
		WatchKey watchKey = path.register(watcherSvc, OVERFLOW,ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
		keyPathMap.put(watchKey, path);
	}
	
	public void removeDirectory(Path path) throws IOException{
		WatchService temp= FileSystems.getDefault().newWatchService();
		WatchKey watchKey=null;
		while((watchKey = watcherSvc.poll())!=null){
			Path tempPath = keyPathMap.remove(watchKey);
			if(tempPath.compareTo(path)!=0){
				watchKey = tempPath.register(temp, OVERFLOW,ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
				keyPathMap.put(watchKey, tempPath);
			}
		}
		watcherSvc=temp;
	}
	
	public void start() throws InterruptedException{
		running=true;
		WatchKey watchKey;
		boolean rename=false;
		Path fileName=null;
		Path path=null;
		ChangeType type=ChangeType.CREATE;
		while (running) {        	
            watchKey=watcherSvc.take();
            for (WatchEvent<?> event: watchKey.pollEvents()) {
                WatchEvent<Path> watchEvent = castEvent(event);
                if(watchEvent.kind()==ENTRY_DELETE){
                	rename=true;
                	fileName=watchEvent.context();
                	path = keyPathMap.get(watchKey);
                	type=ChangeType.DELETE;
                }
                else if(rename&&watchEvent.kind()==ENTRY_CREATE){
                	rename=false;
                	type=ChangeType.RENAME;
                }
                else{
                	fileName=watchEvent.context();
                	path = keyPathMap.get(watchKey);
                	if(watchEvent.kind()==ENTRY_CREATE)
                		type=ChangeType.CREATE;
                	else if(watchEvent.kind()==ENTRY_MODIFY)
                		type=ChangeType.MODIFY;
                }
            }
            client.somethingChanged(path, fileName, type);
            rename=false;
            watchKey.reset();
		}
	}
	
	private <T> WatchEvent<T> castEvent(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }
}
